package com.ruoyi.web.api;

import com.ruoyi.common.config.WechatConfig;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.web.service.SysLoginService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserService;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.util.http.URIUtil;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;

/**
 * 微信授权登录验证
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/wechat")
public class ApiWechatLoginController {
    private static final Logger log = LoggerFactory.getLogger(ApiWechatLoginController.class);
    @Autowired
    private SysLoginService loginService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private WxMpService wxMpService;
    @Autowired
    private WechatConfig wechatConfig;

    /**
     * 微信扫码登陆-生成微信扫描二维码
     * （暂时无法测试，这里需要的是开放平台的appId, 开放平台需要备案网址应用，才能进行扫一扫登录）
     */
    @GetMapping("/wxScanLogin")
    public String wxScanLogin(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取回调url(非必填，只是附带上你扫码之前要进入的网址，具体看业务是否需要)
        String returnUrl = request.getParameter("returnUrl");
        // 拼接扫码登录url: 微信开放平台授权baseUrl  %s相当于?代表占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        //对redirect_url进行URLEncoder编码
        String redirectUrl = wechatConfig.getRedirectURI();
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        //设置%s里面值
        String url = String.format(
                baseUrl,
                wechatConfig.getAppId(),
                redirectUrl,
                returnUrl
        );
        //重定向到请求微信地址里面
        return "redirect:" + url;
    }

    /**
     * 微信公众号登录授权
     */
    @GetMapping("/wxLogin")
    public void wxLogin(@RequestParam("returnUrl") String returnUrl, HttpServletResponse response) throws Exception {
        //这个url的域名必须在公众号中进行注册验证，这个地址是成功后的回调地址
        // 第一步：用户同意授权，获取code, scope：snsapi_base-静默登录 ，snsapi_userinfo-用户确认授权
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize" +
                "?appid=" + wechatConfig.getAppId() +
                "&redirect_uri=" + URLEncoder.encode(wechatConfig.getRedirectURI(), "utf-8") +
                "&response_type=code" +
                "&scope=snsapi_userinfo" +
                "&state=" + returnUrl +
                "#wechat_redirect";
        //必须重定向，否则不能成功
        response.sendRedirect(url);
    }

    /**
     * 微信公众号授权登录
     *
     * @param returnUrl
     * @return
     */
    @GetMapping("/authorize")
    public void authorize(@RequestParam("returnUrl") String returnUrl, HttpServletResponse response) throws Exception {
        String redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(wechatConfig.getRedirectURI(), WxConsts.OAuth2Scope.SNSAPI_USERINFO, URIUtil.encodeURIComponent(returnUrl));
        log.info("获得code redirectUrl{}", redirectUrl);
        //必须重定向，否则不能成功
        response.sendRedirect(redirectUrl);
    }

    /**
     * 微信授权回调接口
     *
     * @return
     * @throws Exception
     */
    @GetMapping("/callback")
    public void callback(@RequestParam("code") String code, @RequestParam("state") String returnUrl, HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("Wechat Callback, code:{}, state:{}", code, returnUrl);
        // 暂时只支持微信，微信通过code获取授权access_token，再获取微信用户信息
        String appId = wechatConfig.getAppId();
        String appSecret = wechatConfig.getAppSecret();
        WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl();
        config.setAppId(appId);
        config.setSecret(appSecret);

        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(config);

        WxOAuth2AccessToken auth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(appId, appSecret, code);
        String openId = auth2AccessToken.getOpenId();
        String unionId = auth2AccessToken.getUnionId();
        // 获取微信用户信息
        WxOAuth2UserInfo userInfo = wxMpService.getOAuth2Service().getUserInfo(auth2AccessToken, "zh_CN"); // 默认中文
        //判断是不是第一次使用微信登录,如果不是第一次，就不需要将信息存入数据库了
        //如果是第一次使用微信登录，那么数据库存储的有(暂时openId即登录用户名)
        //这里根据自己的业务来，我是获取到openId后判断该用户是否存在，如果不存在，那么将该用户新增到数据库中
        SysUser sysUser = sysUserService.selectUserByUserName(openId);
        if (null == sysUser) {
            sysUser = new SysUser();
            sysUser.setUserName(openId);
            sysUser.setNickName(userInfo.getNickname());
            sysUser.setAvatar(userInfo.getHeadImgUrl());
            String initPassword = configService.selectConfigByKey("sys.user.initPassword");
            sysUser.setPassword(SecurityUtils.encryptPassword(initPassword));
            sysUserService.insertUser(sysUser);
        }

        // 生成令牌
        String token = loginService.login(openId, null);
        //必须重定向，否则不能成功
        if (returnUrl.indexOf("?") != -1) {
            // 需要考虑returnUrl中已经带有参数，则往后加参数，
            returnUrl = returnUrl + "&token=" + token;
        } else {
            returnUrl = returnUrl + "?token=" + token;
        }
        response.sendRedirect(returnUrl);
    }
}
